#include "grflib_stdafx.h"
#include "../include/BgImageMesh.h"
#include "../include/VertexFormat.h"
#include "../include/D3D10Engine.h"
#include "../include/grflibEffects.h"
#include "../include/Material.h"
#include "../include/Texture.h"


namespace grflib { namespace engine { namespace aux {

const UINT CBgImageMesh::m_nVertex = 4;
const UINT CBgImageMesh::m_nIndex = 3 /*vertices*/ * 2 /*triangles*/;

using namespace grflib::engine::core;

CBgImageMesh::CBgImageMesh()
	: m_aspectRatio(4.0f / 3.0f)
{
	m_bbox.Cube(0.0f);
	CreateMesh();
	m_nGroups = 1;
}

CBgImageMesh::~CBgImageMesh()
{
	OnDestroy();
}

BBox<float> &CBgImageMesh::GetBoundingBox()
{
	return m_bbox;
}

void CBgImageMesh::CreateMesh()
{
	static float faVertex[m_nVertex * 3] = 
	{
        //-1.0f, -1.0f, 1.0f,
        //-1.0f, 1.0f, 1.0f,
        //1.0f, 1.0f, 1.0f,
        //1.0f, -1.0f, 1.0f
		0.0f, 0.0f, 1.0f,
		0.0f, 1.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
		1.0f, 0.0f, 1.0f
	};

	static UINT faIndex[m_nIndex] = 
	{
		//triangle 0
		0, 1, 2,
		//triangle 1
		0, 2, 3
    };

	static float faTexCoord[m_nVertex * 2] =
	{
		0.0f, 1.0f,
		0.0f, 0.0f,
		1.0f, 0.0f,
		1.0f, 1.0f
	};

	boost::shared_ptr< std::vector<float> > pVertices;
	boost::shared_ptr< std::vector<DWORD> > pIndices;

	boost::shared_ptr<std::vector<D3D10_INPUT_ELEMENT_DESC>> pElements;
	std::size_t stride;

    pVertices.reset( new std::vector<float> );
    pIndices.reset( new std::vector<DWORD> );
    pElements.reset( new std::vector<D3D10_INPUT_ELEMENT_DESC> );
    vertex_format::Mapping(*pElements, stride, 0, 1, 0);

    for (UINT i = 0; i < m_nVertex; i ++)
    {
		//vertex coords
        pVertices->push_back(faVertex[i * 3]);
        pVertices->push_back(faVertex[i * 3+ 1]);
        pVertices->push_back(faVertex[i * 3+ 2]);
		//texture coords
		pVertices->push_back(faTexCoord[i * 2]);
		pVertices->push_back(faTexCoord[i * 2 + 1]);
    }

    for (UINT i = 0; i < m_nIndex; i++)
        pIndices->push_back(faIndex[i]);

    m_vertexVec.push_back(pVertices);
    m_indexVec.push_back(pIndices);
    m_primitiveTopology.push_back(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_vertexLayouts.push_back(pElements);
    m_vertexStride.push_back(stride);
}

void CBgImageMesh::SetMaterial(const std::string &materialName)
{
    m_materials[0] = materialName;
}

HRESULT CBgImageMesh::OnResize(ID3D10Device *pd3dDevice, 
	IDXGISwapChain *pSwapChain,
	const DXGI_SURFACE_DESC *pBufferSurfaceDesc)
{
	//letter box aspect ratio
	float aspectRatio = (float)pBufferSurfaceDesc->Width / (float)pBufferSurfaceDesc->Height;
	float residual = 0.0f;
	UINT stride = 5; //xyz + uv

	if (aspectRatio > m_aspectRatio) //wide letter box
	{
		residual = ((aspectRatio - m_aspectRatio) / 2.0f) / m_aspectRatio;
		//modify texture coords directly
		(*m_vertexVec[0])[stride * 0 + 3] = -residual;
		(*m_vertexVec[0])[stride * 1 + 3] = -residual;
		(*m_vertexVec[0])[stride * 2 + 3] = 1.0f + residual;
		(*m_vertexVec[0])[stride * 3 + 3] = 1.0f + residual;

		(*m_vertexVec[0])[stride * 0 + 4] = 1.0f;
		(*m_vertexVec[0])[stride * 1 + 4] = 0.0f;
		(*m_vertexVec[0])[stride * 2 + 4] = 0.0f;
		(*m_vertexVec[0])[stride * 3 + 4] = 1.0f;
	}
	else if (aspectRatio < m_aspectRatio) //long letter box
	{
		float invAspectRatio = 1.0f / m_aspectRatio;
		residual = ((1.0f / aspectRatio) - invAspectRatio) / 2.0f / invAspectRatio;
		//modify texture coords directly
		(*m_vertexVec[0])[stride * 0 + 3] = 0.0f;
		(*m_vertexVec[0])[stride * 1 + 3] = 0.0f;
		(*m_vertexVec[0])[stride * 2 + 3] = 1.0f;
		(*m_vertexVec[0])[stride * 3 + 3] = 1.0f;

		(*m_vertexVec[0])[stride * 0 + 4] = 1.0f + residual;
		(*m_vertexVec[0])[stride * 1 + 4] = -residual;
		(*m_vertexVec[0])[stride * 2 + 4] = -residual;
		(*m_vertexVec[0])[stride * 3 + 4] = 1.0f + residual;
	}
	else //same aspect ratio, rarely happens
	{
		(*m_vertexVec[0])[stride * 0 + 3] = 0.0f;
		(*m_vertexVec[0])[stride * 1 + 3] = 0.0f;
		(*m_vertexVec[0])[stride * 2 + 3] = 1.0f;
		(*m_vertexVec[0])[stride * 3 + 3] = 1.0f;
		(*m_vertexVec[0])[stride * 0 + 4] = 1.0f;
		(*m_vertexVec[0])[stride * 1 + 4] = 0.0f;
		(*m_vertexVec[0])[stride * 2 + 4] = 0.0f;
		(*m_vertexVec[0])[stride * 3 + 4] = 1.0f;
	}

	UpdateGPUBuffers(pd3dDevice);

	return S_OK;
}

HRESULT CBgImageMesh::PreRender(ID3D10Device *pd3dDevice,
	const D3DXMATRIX *pWorldMat,
	double fTime,
	float fElapsedTime,
    UINT pass)
{
	using namespace grflib::engine::core;
    HRESULT hr;

    bool bSucceeded = false;
    boost::shared_ptr<CEffect> pEffect;

    ////////////////////////////////////////
    //get technique
    if (pEffect = boost::dynamic_pointer_cast<CEffect>(_ResrcMan.FindObj(m_effectName)))
    {
        m_pEffect = pEffect;
        if (m_pTech = pEffect->GetTechnique(m_techniqueName))
        {
            V_RETURN( m_pTech->GetDesc(&m_techDesc) );
            bSucceeded = true;
        }
    }
    if (!bSucceeded)
    {
        W32ERR("Failed to switch to technique: %s/%s", m_effectName.c_str(), m_techniqueName.c_str());
        return E_FAIL;
    }

    return S_OK;
}

///////////////////////////////////////////////////////////////////////
// background image effect
CBgImageEffect::CBgImageEffect()
{
	m_filename = _T("grflibBgImage.fxo");
	AddTechnique("Render");
	AddShaderVariable("BackgroundTexture", CEffect::SVUSE_SHADER_RESOURCE);
}

CBgImageEffect::~CBgImageEffect()
{
}

HRESULT CBgImageEffect::SetShaderVariables(boost::shared_ptr<grflib::engine::core::CMaterial> &pMaterial, const std::string &strTechnique)
{
	HRESULT hr;

	CMaterial::texture_slot_type::iterator iter;
    if ((iter = pMaterial->textureSlots.find("diffuse")) != pMaterial->textureSlots.end())
	{
		boost::shared_ptr<CTexture2D> &pTex = 
			boost::dynamic_pointer_cast<CTexture2D>(_ResrcMan.FindObj(iter->second));
		if (pTex)
		{
			ID3D10EffectShaderResourceVariable *pSRVar
				= GetShaderVariable("BackgroundTexture")->AsShaderResource();
			V_RETURN( pSRVar->SetResource(pTex->GetShaderResourceView()) );
		}
	}
	return S_OK;
}
///////////////////////////////////////////////////////////////////////

}}} //namespaces